home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / chpass / RCS / chpass.c,v < prev    next >
Encoding:
Text File  |  1990-07-12  |  11.0 KB  |  500 lines

  1. head     1.1;
  2. branch   ;
  3. access   ;
  4. symbols  ;
  5. locks    shirriff:1.1; strict;
  6. comment  @ * @;
  7.  
  8.  
  9. 1.1
  10. date     90.07.11.18.15.05;  author shirriff;  state Exp;
  11. branches ;
  12. next     ;
  13.  
  14.  
  15. desc
  16. @@
  17.  
  18.  
  19.  
  20. 1.1
  21. log
  22. @Initial revision
  23. @
  24. text
  25. @/*
  26.  * Copyright (c) 1988 The Regents of the University of California.
  27.  * All rights reserved.
  28.  *
  29.  * Redistribution and use in source and binary forms are permitted
  30.  * provided that the above copyright notice and this paragraph are
  31.  * duplicated in all such forms and that any documentation,
  32.  * advertising materials, and other materials related to such
  33.  * distribution and use acknowledge that the software was developed
  34.  * by the University of California, Berkeley.  The name of the
  35.  * University may not be used to endorse or promote products derived
  36.  * from this software without specific prior written permission.
  37.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  38.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  39.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  40.  */
  41.  
  42. #ifndef lint
  43. char copyright[] =
  44. "@@(#) Copyright (c) 1988 The Regents of the University of California.\n\
  45.  All rights reserved.\n";
  46. #endif /* not lint */
  47.  
  48. #ifndef lint
  49. static char sccsid[] = "@@(#)chpass.c    5.10 (Berkeley) 3/26/89";
  50. #endif /* not lint */
  51.  
  52. #include <sys/param.h>
  53. #include <sys/file.h>
  54. #include <sys/stat.h>
  55. #include <sys/signal.h>
  56. #include <sys/time.h>
  57. #include <sys/resource.h>
  58. #include <pwd.h>
  59. #include <errno.h>
  60. #include <stdio.h>
  61. #include <ctype.h>
  62. #include <chpass.h>
  63. #include <strings.h>
  64.  
  65. char e1[] = ": ";
  66. char e2[] = ":,";
  67.  
  68. int p_change(), p_class(), p_expire(), p_gecos(), p_gid(), p_hdir();
  69. int p_login(), p_passwd(), p_shell(), p_uid();
  70.  
  71. struct entry list[] = {
  72.     { "Login",        p_login,  1,   5, e1,   },
  73.     { "Password",        p_passwd, 1,   8, e1,   },
  74.     { "Uid",        p_uid,    1,   3, e1,   },
  75.     { "Gid",        p_gid,    1,   3, e1,   },
  76.     { "Class",        p_class,  1,   5, e1,   },
  77.     { "Change",        p_change, 1,   6, NULL, },
  78.     { "Expire",        p_expire, 1,   6, NULL, },
  79. #define    E_NAME        7
  80.     { "Full Name",        p_gecos,  0,   9, e2,   },
  81. #define    E_BPHONE    8
  82.     { "Office Phone",    p_gecos,  0,  12, e2,   },
  83. #define    E_HPHONE    9
  84.     { "Home Phone",        p_gecos,  0,  10, e2,   },
  85. #define    E_LOCATE    10
  86.     { "Location",        p_gecos,  0,   8, e2,   },
  87.     { "Home directory",    p_hdir,   1,  14, e1,   },
  88.     { "Shell",        p_shell,  0,   5, e1,   },
  89.     { NULL, 0, },
  90. };
  91.  
  92. uid_t uid;
  93.  
  94. main(argc, argv)
  95.     int argc;
  96.     char **argv;
  97. {
  98.     extern int errno, optind;
  99.     extern char *optarg;
  100.     register char *p;
  101.     struct passwd lpw, *pw;
  102.     struct rlimit rlim;
  103.     FILE *temp_fp;
  104.     int aflag, ch, fd;
  105.     char *fend, *passwd, *temp, *tend;
  106.     char from[MAXPATHLEN], to[MAXPATHLEN];
  107.     char *getusershell();
  108.  
  109.     uid = getuid();
  110.     aflag = 0;
  111.     while ((ch = getopt(argc, argv, "a:")) != EOF)
  112.         switch(ch) {
  113.         case 'a':
  114.             if (uid) {
  115.                 (void)fprintf(stderr,
  116.                     "chpass: %s\n", strerror(EACCES));
  117.                 exit(1);
  118.             }
  119.             loadpw(optarg, pw = &lpw);
  120.             aflag = 1;
  121.             break;
  122.         case '?':
  123.         default:
  124.             usage();
  125.         }
  126.     argc -= optind;
  127.     argv += optind;
  128.  
  129.     if (!aflag)
  130.         switch(argc) {
  131.         case 0:
  132.             if (!(pw = getpwuid(uid))) {
  133.                 (void)fprintf(stderr,
  134.                     "chpass: unknown user: uid %u\n", uid);
  135.                 exit(1);
  136.             }
  137.             break;
  138.         case 1:
  139.             if (!(pw = getpwnam(*argv))) {
  140.                 (void)fprintf(stderr,
  141.                     "chpass: unknown user %s.\n", *argv);
  142.                 exit(1);
  143.             }
  144.             if (uid && uid != pw->pw_uid) {
  145.                 (void)fprintf(stderr,
  146.                     "chpass: %s\n", strerror(EACCES));
  147.                 exit(1);
  148.             }
  149.             break;
  150.         default:
  151.             usage();
  152.         }
  153.  
  154.     (void)signal(SIGHUP, SIG_IGN);
  155.     (void)signal(SIGINT, SIG_IGN);
  156.     (void)signal(SIGQUIT, SIG_IGN);
  157.     (void)signal(SIGTSTP, SIG_IGN);
  158.  
  159.     rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
  160.     (void)setrlimit(RLIMIT_CPU, &rlim);
  161.     (void)setrlimit(RLIMIT_FSIZE, &rlim);
  162.  
  163.     (void)umask(0);
  164.  
  165.     temp = _PATH_PTMP;
  166.     if ((fd = open(temp, O_WRONLY|O_CREAT|O_EXCL, 0600)) < 0) {
  167.         if (errno == EEXIST) {
  168.             (void)fprintf(stderr,
  169.                 "chpass: password file busy -- try again later.\n");
  170.             exit(1);
  171.         }
  172.         (void)fprintf(stderr, "chpass: %s: %s; ",
  173.             temp, strerror(errno));
  174.         goto bad;
  175.     }
  176.     if (!(temp_fp = fdopen(fd, "w"))) {
  177.         (void)fprintf(stderr, "chpass: can't write %s; ", temp);
  178.         goto bad;
  179.     }
  180.  
  181.     if (!aflag && !info(pw))
  182.         goto bad;
  183.  
  184.     /* root should have a 0 uid and a reasonable shell */
  185.     if (!strcmp(pw->pw_name, "root")) {
  186.         if (pw->pw_uid) {
  187.             (void)fprintf(stderr, "chpass: root uid should be 0.");
  188.             exit(1);
  189.         }
  190.         setusershell();
  191.         for (;;)
  192.             if (!(p = getusershell())) {
  193.                 (void)fprintf(stderr,
  194.                     "chpass: warning, unknown root shell.");
  195.                 break;
  196.             }
  197.             else if (!strcmp(pw->pw_shell, p))
  198.                 break;
  199.     }
  200.  
  201.     passwd = _PATH_MASTERPASSWD;
  202.     if (!freopen(passwd, "r", stdin)) {
  203.         (void)fprintf(stderr, "chpass: can't read %s; ", passwd);
  204.         goto bad;
  205.     }
  206.     if (!copy(pw, temp_fp))
  207.         goto bad;
  208.  
  209.     (void)fclose(temp_fp);
  210.     (void)fclose(stdin);
  211.  
  212.     switch(fork()) {
  213.     case 0:
  214.         break;
  215.     case -1:
  216.         (void)fprintf(stderr, "chpass: can't fork; ");
  217.         goto bad;
  218.         /* NOTREACHED */
  219.     default:
  220.         exit(0);
  221.         /* NOTREACHED */
  222.     }
  223.  
  224.     if (makedb(temp)) {
  225.         (void)fprintf(stderr, "chpass: mkpasswd failed; ");
  226. bad:        (void)fprintf(stderr, "%s unchanged.\n", _PATH_MASTERPASSWD);
  227.         (void)unlink(temp);
  228.         exit(1);
  229.     }
  230.  
  231.     /*
  232.      * possible race; have to rename four files, and someone could slip
  233.      * in between them.  LOCK_EX and rename the ``passwd.dir'' file first
  234.      * so that getpwent(3) can't slip in; the lock should never fail and
  235.      * it's unclear what to do if it does.  Rename ``ptmp'' last so that
  236.      * passwd/vipw/chpass can't slip in.
  237.      */
  238.     (void)setpriority(PRIO_PROCESS, 0, -20);
  239.     fend = strcpy(from, temp) + strlen(temp);
  240.     tend = strcpy(to, _PATH_PASSWD) + strlen(_PATH_PASSWD);
  241.     bcopy(".dir", fend, 5);
  242.     bcopy(".dir", tend, 5);
  243.     if ((fd = open(from, O_RDONLY, 0)) >= 0)
  244.         (void)flock(fd, LOCK_EX);
  245.     /* here we go... */
  246.     (void)rename(from, to);
  247.     bcopy(".pag", fend, 5);
  248.     bcopy(".pag", tend, 5);
  249.     (void)rename(from, to);
  250.     bcopy(".orig", fend, 6);
  251.     (void)rename(from, _PATH_PASSWD);
  252.     (void)rename(temp, passwd);
  253.     /* done! */
  254.     exit(0);
  255. }
  256.  
  257. info(pw)
  258.     struct passwd *pw;
  259. {
  260.     struct stat begin, end;
  261.     FILE *fp;
  262.     int fd, rval;
  263.     char *tfile;
  264.  
  265.     tfile = "/tmp/passwd.XXXXXX";
  266.     if ((fd = mkstemp(tfile)) == -1 || !(fp = fdopen(fd, "w+"))) {
  267.         (void)fprintf(stderr, "chpass: no temporary file");
  268.         return(0);
  269.     }
  270.  
  271.     print(fp, pw);
  272.     (void)fflush(fp);
  273.  
  274.     /*
  275.      * give the file to the real user; setuid permissions
  276.      * are discarded in edit()
  277.      */
  278.     (void)fchown(fd, getuid(), getgid());
  279.  
  280.     for (rval = 0;;) {
  281.         (void)fstat(fd, &begin);
  282.         if (edit(tfile)) {
  283.             (void)fprintf(stderr, "chpass: edit failed; ");
  284.             break;
  285.         }
  286.         (void)fstat(fd, &end);
  287.         if (begin.st_mtime == end.st_mtime) {
  288.             (void)fprintf(stderr, "chpass: no changes made; ");
  289.             break;
  290.         }
  291.         (void)rewind(fp);
  292.         if (check(fp, pw)) {
  293.             rval = 1;
  294.             break;
  295.         }
  296.         (void)fflush(stderr);
  297.         if (prompt())
  298.             break;
  299.     }
  300.     (void)fclose(fp);
  301.     (void)unlink(tfile);
  302.     return(rval);
  303. }
  304.  
  305. check(fp, pw)
  306.     FILE *fp;
  307.     struct passwd *pw;
  308. {
  309.     register struct entry *ep;
  310.     register char *p;
  311.     static char buf[1024];
  312.  
  313.     while (fgets(buf, sizeof(buf), fp)) {
  314.         if (!buf[0] || buf[0] == '#')
  315.             continue;
  316.         if (!(p = index(buf, '\n'))) {
  317.             (void)fprintf(stderr, "chpass: line too long.\n");
  318.             return(0);
  319.         }
  320.         *p = '\0';
  321.         for (ep = list;; ++ep) {
  322.             if (!ep->prompt) {
  323.                 (void)fprintf(stderr,
  324.                     "chpass: unrecognized field.\n");
  325.                 return(0);
  326.             }
  327.             if (!strncasecmp(buf, ep->prompt, ep->len)) {
  328.                 if (ep->restricted && uid)
  329.                     break;
  330.                 if (!(p = index(buf, ':'))) {
  331.                     (void)fprintf(stderr,
  332.                         "chpass: line corrupted.\n");
  333.                     return(0);
  334.                 }
  335.                 while (isspace(*++p));
  336.                 if (ep->except && strpbrk(p, ep->except)) {
  337.                     (void)fprintf(stderr,
  338.                        "chpass: illegal character in the \"%s\" field.\n",
  339.                         ep->prompt);
  340.                     return(0);
  341.                 }
  342.                 if ((ep->func)(p, pw, ep))
  343.                     return(0);
  344.                 break;
  345.             }
  346.         }
  347.     }
  348.     /*
  349.      * special checks...
  350.      *
  351.      * there has to be a limit on the size of the gecos fields,
  352.      * otherwise getpwent(3) can choke.
  353.      * ``if I swallow anything evil, put your fingers down my throat...''
  354.      *    -- The Who
  355.      */
  356.     if (strlen(list[E_NAME].save) + strlen(list[E_BPHONE].save) +
  357.         strlen(list[E_HPHONE].save) + strlen(list[E_LOCATE].save)
  358.         > 512) {
  359.         (void)fprintf(stderr, "chpass: gecos field too large.\n");
  360.         exit(1);
  361.     }
  362.     (void)sprintf(pw->pw_gecos = buf, "%s,%s,%s,%s",
  363.         list[E_NAME].save, list[E_LOCATE].save, list[E_BPHONE].save,
  364.         list[E_HPHONE].save);
  365.     return(1);
  366. }
  367.  
  368. copy(pw, fp)
  369.     struct passwd *pw;
  370.     FILE *fp;
  371. {
  372.     register int done;
  373.     register char *p;
  374.     char buf[1024];
  375.  
  376.     for (done = 0; fgets(buf, sizeof(buf), stdin);) {
  377.         /* skip lines that are too big */
  378.         if (!index(buf, '\n')) {
  379.             (void)fprintf(stderr, "chpass: line too long; ");
  380.             return(0);
  381.         }
  382.         if (done) {
  383.             (void)fprintf(fp, "%s", buf);
  384.             continue;
  385.         }
  386.         if (!(p = index(buf, ':'))) {
  387.             (void)fprintf(stderr, "chpass: corrupted entry; ");
  388.             return(0);
  389.         }
  390.         *p = '\0';
  391.         if (strcmp(buf, pw->pw_name)) {
  392.             *p = ':';
  393.             (void)fprintf(fp, "%s", buf);
  394.             continue;
  395.         }
  396.         (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
  397.             pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
  398.             pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
  399.             pw->pw_dir, pw->pw_shell);
  400.         done = 1;
  401.     }
  402.     if (!done)
  403.         (void)fprintf(fp, "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n",
  404.             pw->pw_name, pw->pw_passwd, pw->pw_uid, pw->pw_gid,
  405.             pw->pw_class, pw->pw_change, pw->pw_expire, pw->pw_gecos,
  406.             pw->pw_dir, pw->pw_shell);
  407.     return(1);
  408. }
  409.  
  410. makedb(file)
  411.     char *file;
  412. {
  413.     int status, pid, w;
  414.  
  415.     if (!(pid = vfork())) {
  416.         execl(_PATH_MKPASSWD, "mkpasswd", "-p", file, NULL);
  417.         _exit(127);
  418.     }
  419.     while ((w = wait(&status)) != pid && w != -1);
  420.     return(w == -1 || status);
  421. }
  422.  
  423. edit(file)
  424.     char *file;
  425. {
  426.     int status, pid, w;
  427.     char *p, *editor, *getenv();
  428.  
  429.     if (editor = getenv("EDITOR")) {
  430.         if (p = rindex(editor, '/'))
  431.             ++p;
  432.         else
  433.             p = editor;
  434.     }
  435.     else
  436.         p = editor = "vi";
  437.     if (!(pid = vfork())) {
  438.         (void)setgid(getgid());
  439.         (void)setuid(getuid());
  440.         execlp(editor, p, file, NULL);
  441.         _exit(127);
  442.     }
  443.     while ((w = wait(&status)) != pid && w != -1);
  444.     return(w == -1 || status);
  445. }
  446.  
  447. loadpw(arg, pw)
  448.     char *arg;
  449.     register struct passwd *pw;
  450. {
  451.     register char *cp;
  452.     long atol();
  453.     char *strsep();
  454.  
  455.     pw->pw_name = strsep(arg, ":");
  456.     pw->pw_passwd = strsep((char *)NULL, ":");
  457.     if (!(cp = strsep((char *)NULL, ":")))
  458.         goto bad;
  459.     pw->pw_uid = atoi(cp);
  460.     if (!(cp = strsep((char *)NULL, ":")))
  461.         goto bad;
  462.     pw->pw_gid = atoi(cp);
  463.     pw->pw_class = strsep((char *)NULL, ":");
  464.     if (!(cp = strsep((char *)NULL, ":")))
  465.         goto bad;
  466.     pw->pw_change = atol(cp);
  467.     if (!(cp = strsep((char *)NULL, ":")))
  468.         goto bad;
  469.     pw->pw_expire = atol(cp);
  470.     pw->pw_gecos = strsep((char *)NULL, ":");
  471.     pw->pw_dir = strsep((char *)NULL, ":");
  472.     pw->pw_shell = strsep((char *)NULL, ":");
  473.     if (!pw->pw_shell || strsep((char *)NULL, ":")) {
  474. bad:        (void)fprintf(stderr, "chpass: bad password list.\n");
  475.         exit(1);
  476.     }
  477. }
  478.  
  479. prompt()
  480. {
  481.     register int c;
  482.  
  483.     for (;;) {
  484.         (void)printf("re-edit the password file? [y]: ");
  485.         (void)fflush(stdout);
  486.         c = getchar();
  487.         if (c != EOF && c != (int)'\n')
  488.             while (getchar() != (int)'\n');
  489.         return(c == (int)'n');
  490.     }
  491.     /* NOTREACHED */
  492. }
  493.  
  494. usage()
  495. {
  496.     (void)fprintf(stderr, "usage: chpass [-a list] [user]\n");
  497.     exit(1);
  498. }
  499. @
  500.